跳到主要内容

27 再论智能指针(上)

再论智能指针

  • 思考

    使用智能指针(SmartPointer)替换单链表(LinkList)中的原生指针是否可行?

  • 问题出在哪里?

    • SmartPointer的设计方案
      • 指针生命周期结束时主动释放堆空间
      • 一片堆空间最多只能由一个指针标识
      • 杜绝指针运算和指针比较
  • 新的设计方案 是时候创建新的智能指针了!

  • 新的设计方案
    • Pointer是智能指针的抽象父类(模板)
      • 纯虚析构函数 virtual ~Pointer() = 0;(Pointer需要被继承才能产生可用类)
      • 重载operator -> ()
      • 重载operator * ()
    template<typename T>
    class Pointer:public Object
    {
    public:
    Pointer(T *p = nullptr);
    T* operator -> ();
    T& operator * ();
    bool isNull();
    T* get();

    protected:
    T* m_pointer;
    };

编程实验

  • 智能指针的新方案

    //Pointer
    #ifndef POINTER_H
    #define POINTER_H

    #include "Object.h"

    namespace KylinLib {

    template<typename T>
    class Pointer : public Object{
    public:
    T* get() const{
    return m_pointer;
    }

    T& operator *() const{
    return *m_pointer;
    }

    T* operator ->() const{
    return m_pointer;
    }

    bool isNull() const{
    return (m_pointer==nullptr);
    }
    virtual ~Pointer() = 0;
    protected:
    T *m_pointer = nullptr;
    };
    }
    #endif // POINTER_H
    //SmartPointer.h
    #ifndef SMARTPOINTER_H
    #define SMARTPOINTER_H

    #include "Pointer.h"

    namespace KylinLib {

    template<typename T>
    class SmartPointer : public Pointer<T>
    {
    public:
    SmartPointer(T *p){
    this->m_pointer = p;
    }

    SmartPointer(const SmartPointer &p){
    this->m_pointer = p.m_pointer;
    const_cast<SmartPointer<T>&>(p).m_pointer = nullptr;
    }
    ~SmartPointer(){
    delete this->m_pointer;
    }
    SmartPointer& operator=(const SmartPointer &p){
    if(this!=&p){
    if(this->m_pointer!=nullptr) delete this->m_pointer;
    const_cast<SmartPointer<T>&>(p).m_pointer = nullptr;
    p.m_pointer = nullptr;
    }
    return *this;
    }
    };

    }


    #endif // SMARTPOINTER_H

思考:如何实现SharedPointer使得多个智能指针对象可以指向同一片堆内存,同时支持堆内存的自动释放?

28 再论智能指针(下)

再论智能指针

  • 课程目标
    • 完成SharedPointer类的具体实现

  • SharedPointer设计要点
    • 类模板
      • 通过计数机制(ref)标识堆内存
        • 堆内存被指向时:ref++
        • 指针被置空时:ref--
        • ref==0时:释放堆空间
  • 计数机制原理剖析

  • SharedPointer类的声明

    template<typename T>
    class SharedPointer : public Pointer<T>
    {
    public:
    SharedPointer(T *p = nullptr);
    SharedPointer(const SharedPointer<T>& obj);
    SharedPointer<T>& operator= (SharedPointer<T> &obj);
    void clear(); //将当前指针置为空
    ~SharedPointer();
    protected:
    int *m_ref; //计数机制成员指针
    }
  • 智能指针的比较 由于SharedPointer支持多个对象同时指向一片堆空间;因此,必须支持比较操作!

编程实验

  • 智能指针的新成员

    //SharedPointer.h
    #ifndef SHAREDPOINTER_H
    #define SHAREDPOINTER_H

    #include "Pointer.h"

    namespace KylinLib {
    template<typename T>
    class SharedPointer : public Pointer<T>{
    public:
    SharedPointer(T *p){
    if(p==nullptr) return;
    this->m_pointer = p;
    m_counter = new size_t(1);
    }
    SharedPointer(const SharedPointer &obj){
    if(obj.m_counter==nullptr) return;
    this->m_counter = obj.m_counter;
    this->m_pointer = obj.m_pointer;
    (*m_counter)++;
    }

    SharedPointer& operator=(const SharedPointer &obj){
    if(this!=&obj){
    clear();
    this->m_counter = obj.m_counter;
    this->m_pointer = obj.m_pointer;
    (*m_counter)++;
    }
    }

    void clear(){
    if(m_counter==nullptr) return;
    (*m_counter)--;
    if(*m_counter == 0){
    delete m_counter;
    delete this->m_pointer;
    }
    this->m_pointer = nullptr;
    m_counter = nullptr;
    }

    // bool operator==(const SharedPointer &obj){
    // return (this->m_pointer==obj.m_pointer);
    // }

    ~SharedPointer(){
    clear();
    }
    private:
    size_t *m_counter = nullptr;
    };

    template <typename T>
    bool operator== (const SharedPointer<T> l,const SharedPointer<T> r){
    return (l.get()==r.get());
    }

    template <typename T>
    bool operator!= (const SharedPointer<T> l,const SharedPointer<T> r){
    return !(l==r);
    }


    }
    #endif // SHAREDPOINTER_H
  • 智能指针的使用军规

    • 只能用来指向堆空间的单个变量(对象)
    • 不同类型的智能指针对象不能混合使用
    • 不要使用delete释放智能指针指向的堆空间

小结

  • SharedPointer最大程度的模拟了原生指针的行为
  • 计数机制确保多个智能指针合法的指向同一片堆空间
  • 智能指针只能用于指向堆空间中的内存
  • 不同类型的智能指针不要混合使用
  • 堆对象的生命周期由智能指针进行管理

练习:用SharedPointer替换LinkedList中的原生指针。

思考:在KylinLib内部是否需要使用智能指针?为什么?